#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>

#define BEEP_CNT 1
#define BEEP_NAME "dtbbeep"
#define BEEP_MAJOR 200
#define BEEP_MINOR 0

#define BEEP_ON  1
#define BEEP_OFF 0

static void __iomem *CCM_CCGR1_VIRT;
static void __iomem *SW_MUX_CTL_PAD_SNVS_TAMPER1_VIRT;
static void __iomem *SW_PAD_CTL_PAD_SNVS_TAMPER1_VIRT;
static void __iomem *GPIO5_DR_VIRT;
static void __iomem *GPIO5_GDIR_VIRT;

struct beep_dev{
	dev_t devid;		// 设备号
	struct cdev cdev;	// cdev
	struct class *class;	// 类
	struct device *device;	// 设备
	struct device_node *nd;	// 设备节点
    int major;
    int minor;	
};
struct beep_dev beep;

void beep_switch(u8 sta)
{
    u32 val=0;
    val = readl(GPIO5_GDIR_VIRT);
    if(sta == BEEP_ON){
        val = readl(GPIO5_DR_VIRT);
        val &= ~(1<<1);
        writel(val, GPIO5_DR_VIRT);
    }
    else if(sta == BEEP_OFF){
        val = readl(GPIO5_DR_VIRT);
        val |= 1<<1;
        writel(val, GPIO5_DR_VIRT);
    }
}
    
static int beep_open(struct inode *inode, struct file *filp)
{
	filp->private_data = &beep;
	return 0;
}

static ssize_t beep_read(struct file *filp, char __user *buf, size_t count, loff_t *offt)
{
	return 0;
}

static ssize_t beep_write(struct file *filp, const char __user *buf, size_t count, loff_t *offt)
{
    int ret = 0;
    unsigned char beepdata[1];
    ret = copy_from_user(beepdata, buf, count);
    if(ret < 0){
        printk("read data error\r\n");
        return -EFAULT;
    }
    if(beepdata[0] == BEEP_OFF){
        beep_switch(BEEP_OFF);
    }
    else if(beepdata[0] == BEEP_ON){
        beep_switch(BEEP_ON);
    }
    printk("beep write data = %c\r\n", beepdata[0]);
    return 0;
}

static int beep_release(struct inode *inode, struct file *filp)
{
	return 0;
}

static struct file_operations beep_fops = {
	.owner = THIS_MODULE,
	.open = beep_open,
	.read = beep_read,
	.write = beep_write,
	.release = beep_release,
};

static int __init beep_init(void)
{
    int ret = 0;
    u32 val;
    u32 regdata[14];
    const char *sta;
    struct property *pro;
    
    beep.nd = of_find_node_by_path("/minibeep");
    if(beep.nd == NULL){
        printk("beep node err\r\n");
        return -EINVAL;
    }

    pro = of_find_property(beep.nd, "compatible", NULL);
    if(pro == NULL){
        printk("status err\r\n");
        return -EINVAL;
    }

    ret = of_property_read_string(beep.nd, "status", &sta);
    if(ret < 0){
        printk("status err\r\n");
        return -EINVAL;
    }

    ret= of_property_read_u32_array(beep.nd, "reg", regdata, 10);
    if(ret < 0){
        printk("reg err\r\n");
        return -EINVAL;
    }
    else{
        u8 i=0;
        printk("regdata");
        for ( i = 0; i < 10; i++)
        {
           printk("%#x",regdata[i]);
        }
        printk("\r\n");
    }
    printk("111");
    
    CCM_CCGR1_VIRT = ioremap(regdata[0], regdata[1]);
    SW_MUX_CTL_PAD_SNVS_TAMPER1_VIRT = ioremap(regdata[2], regdata[3]);
    SW_PAD_CTL_PAD_SNVS_TAMPER1_VIRT = ioremap(regdata[4], regdata[5]);
    GPIO5_DR_VIRT = ioremap(regdata[6], regdata[7]);
    GPIO5_GDIR_VIRT = ioremap(regdata[8], regdata[9]);

    val = readl(CCM_CCGR1_VIRT);
    val &= ~(3<<30);
    val |= (3<<30);
    writel(val, CCM_CCGR1_VIRT);

    writel(0x0, SW_MUX_CTL_PAD_SNVS_TAMPER1_VIRT);
    writel(0x10B0, SW_PAD_CTL_PAD_SNVS_TAMPER1_VIRT);

    val = readl(GPIO5_GDIR_VIRT);
    val |= 1<<1;
    writel(val, GPIO5_GDIR_VIRT);
    beep_switch(BEEP_OFF);
    printk("beep init ok\r\n");
 
    ret = alloc_chrdev_region(&beep.devid, 0, BEEP_CNT, BEEP_NAME);
    if(ret < 0){
        printk("alloc chrdev failed\r\n");
        return -EINVAL;
    }
    beep.major = MAJOR(beep.devid);
    beep.minor = MINOR(beep.devid);
    beep.class = class_create(THIS_MODULE, BEEP_NAME);
    if(IS_ERR(beep.class)){
        printk("create class failed\r\n");
        return -EINVAL;
    }
    beep.device = device_create(beep.class, NULL, beep.devid, NULL, BEEP_NAME);
    if(IS_ERR(beep.device)){
        printk("create device failed\r\n");
        return -EINVAL;
    }
    cdev_init(&beep.cdev, &beep_fops);
    cdev_add(&beep.cdev, beep.devid, BEEP_CNT);
    printk("major = %d, minor = %d\r\n", beep.major, beep.minor);
    printk("beep init ok\r\n");
    return 0;


};

static void __exit beep_exit(void)
{
    iounmap(CCM_CCGR1_VIRT);
    iounmap(SW_MUX_CTL_PAD_SNVS_TAMPER1_VIRT);
    iounmap(SW_PAD_CTL_PAD_SNVS_TAMPER1_VIRT);
    iounmap(GPIO5_DR_VIRT);
    iounmap(GPIO5_GDIR_VIRT);
    unregister_chrdev_region(beep.devid, BEEP_CNT);
    cdev_del(&beep.cdev);
    device_destroy(beep.class, beep.devid);
    class_destroy(beep.class);
    printk("beep exit ok\r\n");
};

module_init(beep_init);
module_exit(beep_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("xby");
